﻿using System;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Perspective.Wpf3D.Primitives;
using Perspective.Core.Primitives;
using Perspective.Core.Wpf;
using System.Windows;

namespace Perspective.Wpf3D.Sculptors
{
    /// <summary>
    /// 组成管子的所有点和三角形集合
    /// A class to handle points and triangles of a 3D pipe model.
    /// Default radius is 10.0.
    /// </summary>
    public class PipeSculptor : Sculptor
    {
        #region Constant
        internal const double DefaultOuterCircleRadius = 1.0;
        internal const double DefaultInnerCircleRadius = 0.5;
        internal const double DefaultHeight = 1.0;
        #endregion

        #region Members
        private int _circumferenceSideCount;
        private double _initialAngle;
        private double _outerCircleRadius = DefaultOuterCircleRadius;
        private double _innerCircleRadius = DefaultInnerCircleRadius;
        private double _height = DefaultHeight;
        private double _roundingRate;
        private ConcentricPolygonSculptor _ps1;
        private ConcentricPolygonSculptor _ps2;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of PipeSculptor.
        /// </summary>
        public PipeSculptor()
        {
            //TextureOnSide = true;
            TexturePosition = TexturePositions.OnSides;
        }

        /// <summary>
        /// Initializes a new instance of PipeSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public PipeSculptor(int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(circumferenceSideCount, initialAngle, roundingRate);
        }

        /// <summary>
        /// Initializes a new instance of PipeSculptor.
        /// </summary>
        /// <param name="outerCircleRadius">Circumcircle Radius</param>
        /// <param name="innerCircleRadius ">Circumcircle Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public PipeSculptor(double outerCircleRadius, double innerCircleRadius, double height, int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(outerCircleRadius, innerCircleRadius, height, circumferenceSideCount, initialAngle, roundingRate);
        }

        /// <summary>
        /// Initializes an existing instance of PipeSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public void Initialize(int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(DefaultOuterCircleRadius, DefaultInnerCircleRadius, DefaultHeight, circumferenceSideCount, initialAngle, roundingRate);
        }

        /// <summary>
        /// Initializes an existing instance of PipeSculptor.
        /// </summary>
        /// <param name="outerCircleRadius">Circumcircle Radius</param>
        /// <param name="innerCircleRadius ">Circumcircle Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public void Initialize(double outerCircleRadius, double innerCircleRadius, double height, int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            _circumferenceSideCount = circumferenceSideCount;
            _initialAngle = initialAngle;
            _roundingRate = roundingRate;
            _outerCircleRadius = outerCircleRadius;
            _innerCircleRadius = innerCircleRadius;
            _height = height;
        }
        #endregion

        #region Properties

        #region TexturePosition 纹理坐标位置

        /// <summary>
        /// Gets or sets the position a a texture. Default is OnSides.
        /// </summary>
        public TexturePositions TexturePosition { get; set; }

        #endregion

        #region PolygonSculptor1 管子下表面同心多边形

        /// <summary>
        /// Gets the 1st PolygonSculptor.
        /// </summary>
        public ConcentricPolygonSculptor PolygonSculptor1
        {
            get { return _ps1; }
        }

        #endregion

        #region PolygonSculptor2 管子上表面同心多边形

        /// <summary>
        /// Gets the 2nd PolygonSculptor.
        /// </summary>
        public ConcentricPolygonSculptor PolygonSculptor2
        {
            get { return _ps2; }
        }

        #endregion

        #endregion

        #region Functions

        /// <summary>
        /// Initializes the Points and Triangles collections.
        /// Called By Sculptor.BuildMesh()
        /// </summary>
        protected override void CreateTriangles()
        {
            _ps1 = new ConcentricPolygonSculptor(_outerCircleRadius, _innerCircleRadius, _circumferenceSideCount, _initialAngle, _roundingRate);
            //_ps1.RoundingRate = _roundingRate;
            _ps1.BuildTriangles(TriangleSideKind.Back);
            foreach (Point3D p in _ps1.Points)
            {
                this.Points.Add(p);
            }
            foreach (Point3DTriplet tpl in _ps1.Triangles)
            {
                this.Triangles.Add(tpl);
            }
          
            _ps2 = new ConcentricPolygonSculptor(_outerCircleRadius, _innerCircleRadius, _circumferenceSideCount, _initialAngle, _roundingRate);
            //_ps2.Center = new Point3D(
            //    _ps2.Center.X,
            //    _ps2.Center.Y,
            //    _ps2.Center.Z + _height);
            for (int i = 0; i < _ps2.Points.Count; i++)
            {
                _ps2.Points[i] = new Point3D(
                    _ps2.Points[i].X,
                    _ps2.Points[i].Y,
                    _ps2.Points[i].Z + _height);
                this.Points.Add(_ps2.Points[i]);
            }
            //_ps2.RoundingRate = _roundingRate;
            _ps2.BuildTriangles(TriangleSideKind.Front);

            foreach (Point3DTriplet tpl in _ps2.Triangles)
            {
                this.Triangles.Add(tpl);
            }

            int outerSideCount = _ps1.Points.Count / 2;

            for (int i = 0; i <= outerSideCount-1; i++)
            {
                if (i < outerSideCount - 1)
                {
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[i], _ps2.Points[i + 1], _ps2.Points[i]));
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[i], _ps1.Points[i + 1], _ps2.Points[i + 1]));
                }
                else
                {
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[i], _ps2.Points[0], _ps2.Points[i]));
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[i], _ps1.Points[0], _ps2.Points[0]));
                }
            }

            for (int j = outerSideCount; j <= _ps1.Points.Count - 1; j++)
            {
                if (j < _ps1.Points.Count - 1)
                {
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[j], _ps2.Points[j], _ps2.Points[j + 1]));
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[j], _ps2.Points[j + 1], _ps1.Points[j + 1]));
                }
                else
                {
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[j], _ps2.Points[j], _ps2.Points[outerSideCount]));
                    this.Triangles.Add(new Point3DTriplet(_ps1.Points[j], _ps2.Points[outerSideCount], _ps1.Points[outerSideCount]));
                }
            }
        }

        /// <summary>
        /// A method for building the TextureCoordinates collection of the mesh.
        /// </summary>
        internal override void MapTexture()
        {
            Mesh.TextureCoordinates.Clear();
            if (TexturePosition == TexturePositions.OnEnds)
            {
                foreach (Point3DTriplet pt in _ps1.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(
                            new Point(p.X, -p.Y));
                    }
                }
                foreach (Point3DTriplet pt in _ps2.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(
                            new Point(p.X, -p.Y));
                    }
                }
            }
            else if (TexturePosition == TexturePositions.OnSides)
            {
                double minY = 0.0;
                double maxY = 1.0;
                double minX = 0.0;
                double maxX;
                double xPos = 0.0;
                double segmentLength = 1.0;

                foreach (Point3DTriplet pt in _ps1.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(new Point(0.0, 0.0));
                    }
                }

                foreach (Point3DTriplet pt in _ps2.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(new Point(0.0, 0.0));
                    }
                }

                for (int i = 0; i <= _ps1.Points.Count - 1; i++)
                {
                    if (_roundingRate != 0.0)
                    {
                        Point3D p2;
                        if (i == _ps1.Points.Count - 1)
                        {
                            p2 = _ps1.Points[0];
                        }
                        else
                        {
                            p2 = _ps1.Points[i + 1];
                        }
                        Vector3D v = p2 - _ps1.Points[i];
                        segmentLength = v.Length;
                    }
                    minX = xPos;
                    xPos += segmentLength;
                    maxX = xPos;

                    Mesh.TextureCoordinates.Add(new Point(minX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, minY));
                    Mesh.TextureCoordinates.Add(new Point(minX, minY));

                    Mesh.TextureCoordinates.Add(new Point(minX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, minY));
                }
            }
        }

        #endregion
    }
}
